#pragma once

#include <iostream>
#include <cstdlib>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

const int gdefaultsockfd = -1;
const int gbacklog = 8;

namespace SocketModule
{
	class Socket;
	using SockPtr = std::shared_ptr<Socket>;

	using namespace LogModule;

	// 基类，规定创建Socket的方法
	// 提供若干个固定模式的socket方法
	// 模版方法模式
	class Socket
	{
	public:
		~Socket() = default;
		virtual void SocketOrDie() = 0; // 创建成功返回，失败死亡
		virtual void SetSocketOpt() = 0; // 设置套接字相关属性
		virtual bool BindOrDie(int port) = 0; // 绑定端口
		virtual bool ListenOrDie() = 0;
		virtual SockPtr ToAccept(InetAddr *client) = 0; // 接收连接
		virtual void Close() = 0;				// 关闭套接字
		virtual int Recv(std::string *out) = 0;
		virtual int Send(const std::string &in) = 0;
		virtual int Fd() = 0;					// 获取文件描述符

		// 模版方法
		// 提供一个创建listensockfd的固定套路
		void BuildTcpSocket(int port)
		{
			SocketOrDie();
			SetSocketOpt();
			BindOrDie(port);
			ListenOrDie();
		}
	};

	class TcpSocket : public Socket
	{
	public:
		TcpSocket()
			: _sockfd(gdefaultsockfd)
		{
		}
		TcpSocket(int sockfd)
			: _sockfd(sockfd)
		{
		}
		~TcpSocket()
		{
		}
		virtual void SocketOrDie() override
		{
			_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
			if (_sockfd < 0)
			{
				LOG(LogLevel::ERROR) << "socket error";
				exit(SOCKET_ERR);
			}
			LOG(LogLevel::DEBUG) << "socket create success: " << _sockfd;
		}
		virtual void SetSocketOpt() override
		{
			// 保证我们的服务器，异常断开之后，可以立即重启，不会有bind失败问题
			int opt = 1;
			int n = ::setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
			(void)n;
		}
		virtual bool BindOrDie(int port)
		{
			if (_sockfd == gdefaultsockfd)
				return false;

			InetAddr addr(port);
			int n = ::bind(_sockfd, addr.NetAddr(), addr.NetAddrLen());
			if (n < 0)
			{
				LOG(LogLevel::ERROR) << "bind error";
				exit(BIND_ERR);
			}
			LOG(LogLevel::DEBUG) << "bind success: " << _sockfd;

			return true;
		}
		virtual bool ListenOrDie() override
		{
			if (_sockfd == gdefaultsockfd)
				return false;

			int n = ::listen(_sockfd, gbacklog);
			if (n < 0)
			{
				LOG(LogLevel::ERROR) << "listen error";
				exit(LISTEN_ERR);
			}
			LOG(LogLevel::DEBUG) << "listen success: " << _sockfd;
			return true;
		}

		// 1.文件描述符 2.client info
		// 接收客户端连接
		virtual SockPtr ToAccept(InetAddr *client) override
		{
			if (!client)
				return nullptr;

			struct sockaddr_in peer;
			socklen_t len = sizeof(peer);
			int newsockfd = ::accept(_sockfd, CONV(&peer), &len);
			if (newsockfd < 0)
			{
				LOG(LogLevel::ERROR) << "accept error";
				return nullptr;
			}
			client->SetAddr(peer, len);
			return std::make_shared<TcpSocket>(newsockfd);
		}
		virtual void Close() override
		{
			if (_sockfd == gdefaultsockfd)
				return;
			::close(_sockfd);
		}

		virtual int Recv(std::string *out) override
		{
			char buffer[1024];
			auto size = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
			if (size > 0)
			{
				buffer[size] = 0;
				*out = buffer;
			}
			return size;
		}
		virtual int Send(const std::string &in) override
		{
			auto size = ::send(_sockfd, in.c_str(), in.size(), 0);
			return size;
		}

		virtual int Fd() override
		{
			return _sockfd;
		}

	private:
		int _sockfd;
	};

	// int main()
	// {
	// 	Socket *sk = new TcpSocket();
	// 	sk->BuildTcpSocket(8080);
	// }
}